package com.tianji.remark.service.impl;

import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.dto.remark.LikedTimesDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.utils.CollUtils;
import com.tianji.common.utils.StringUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.remark.constant.RedisConstants;
import com.tianji.remark.domain.dto.LikeRecordFormDTO;
import com.tianji.remark.domain.po.LikedRecord;
import com.tianji.remark.mapper.LikedRecordMapper;
import com.tianji.remark.service.ILikedRecordService;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.connection.StringRedisConnection;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import static com.tianji.common.constants.MqConstants.Exchange.LIKE_RECORD_EXCHANGE;
import static com.tianji.common.constants.MqConstants.Key.LIKED_TIMES_KEY_TEMPLATE;

/**
 * <p>
 * 点赞记录表 服务实现类
 * </p>
 */
@Service
@RequiredArgsConstructor
public class LikedRecordServiceRedisImpl extends ServiceImpl<LikedRecordMapper, LikedRecord> implements ILikedRecordService {

    private final RabbitMqHelper mqHelper;

    private final StringRedisTemplate redisTemplate;

    /**
     * 点赞或取消点赞
     * @param recordFormDTO
     */
    @Override
    public void addLikeRecord(LikeRecordFormDTO recordFormDTO) {
        // 1.基于前端的参数，判断是执行点赞还是取消点赞
        boolean success = recordFormDTO.getLiked() ? like(recordFormDTO) : unlike(recordFormDTO);
        // 2.判断是否执行成功，如果失败，则直接结束
        if (!success) {
            return;
        }
        // 3.如果执行成功，统计点赞总数
        Long likedTimes = redisTemplate.opsForSet()
                .size(RedisConstants.LIKE_BIZ_KEY_PREFIX + recordFormDTO.getBizId());
        if (null == likedTimes) {
            return;
        }
        // 4.缓存点总数到redis
        redisTemplate.opsForZSet().add(
                RedisConstants.LIKE_BIZ_KEY_PREFIX + recordFormDTO.getBizType(),
                recordFormDTO.getBizId().toString(),
                likedTimes
        );
    }

    /**
     * 查询指定业务id的点赞状态
     * @param bizIds
     * @return
     */
    @Override
    public Set<Long> isBizLiked(List<Long> bizIds) {
        // 1.获取用户id
        Long userId = UserContext.getUser();
        // 2.查询点赞状态
        List<Object> objects = redisTemplate.executePipelined((RedisCallback<Object>) connection -> {
            StringRedisConnection src = (StringRedisConnection) connection;
            for (Long bizId : bizIds) {
                String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + bizId;
                src.sIsMember(key, userId.toString());
            }
            return null;
        });
        // 3.返回结果
        return IntStream.range(0, objects.size())       // 创建从0到集合size的流
                .filter(i -> (boolean) objects.get(i))  // 遍历每个元素，保留结果为true的角标i
                .mapToObj(bizIds::get)                  // 用角标i取bizIds中的对应数据，就是点赞过的id
                .collect(Collectors.toSet());           // 收集
    }

    /**
     * 定时任务：检查并发送点赞次数
     * @param bizType
     * @param maxBizSize
     */
    @Override
    public void readLikedTimesAndSendMessage(String bizType, int maxBizSize) {
        // 1.读取并移除Redis中缓存的点赞总数
        String key = RedisConstants.LIKES_TIMES_KEY_PREFIX + bizType;
        Set<ZSetOperations.TypedTuple<String>> tuples = redisTemplate.opsForZSet().popMin(key, maxBizSize);
        if (CollUtils.isEmpty(tuples)) {
            return;
        }
        // 2.数据转换
        List<LikedTimesDTO> list = new ArrayList<>(tuples.size());
        for (ZSetOperations.TypedTuple<String> tuple : tuples) {
            String bizId = tuple.getValue();
            Double likeTimes = tuple.getScore();
            if (null == bizId || null == likeTimes) {
                continue;
            }
            list.add(LikedTimesDTO.of(Long.valueOf(bizId), likeTimes.intValue()));
        }
        // 3.发送MQ消息
        mqHelper.send(
                LIKE_RECORD_EXCHANGE,
                StringUtils.format(LIKED_TIMES_KEY_TEMPLATE, bizType),
                list
        );
    }

    private boolean like(LikeRecordFormDTO recordFormDTO) {
        // 1.获取用户Id：从用户上下文中获取当前登录用户的ID
        Long userId = UserContext.getUser();

        // 2.获取Key：拼接Redis的Key，格式为"likes:业务类型:业务ID"
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + recordFormDTO.getBizId();

        // 3.执行SADD命令：将用户ID添加到Redis的Set集合中
        // 如果用户已经点赞过，返回0；如果是新点赞，返回1
        Long result = redisTemplate.opsForSet().add(key, userId.toString());

        // 4.返回结果：判断操作是否成功
        // 如果result不为null且大于0，表示点赞成功
        return result != null && result > 0;
    }


    private boolean unlike(LikeRecordFormDTO recordFormDTO) {
        // 1.获取用户Id：从用户上下文中获取当前登录用户的ID
        Long userId = UserContext.getUser();

        // 2.获取Key：拼接Redis的Key，格式为"likes:业务类型:业务ID"
        String key = RedisConstants.LIKE_BIZ_KEY_PREFIX + recordFormDTO.getBizId();

        // 3.执行SREM命令：从Redis的Set集合中移除用户ID
        // 如果用户已经取消点赞过，返回0；如果是成功取消点赞，返回1
        Long result = redisTemplate.opsForSet().remove(key, userId.toString());

        // 4.返回结果：判断操作是否成功
        // 如果result不为null且大于0，表示取消点赞成功
        return result != null && result > 0;
    }

}
